/* bzflag
 * Copyright (c) 1993-2020 Tim Riker
 *
 * This package is free software;  you can redistribute it and/or
 * modify it under the terms of the license found in the file
 * named COPYING that should have accompanied this file.
 *
 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

/*
 * Communication protocol constants
 */

#ifndef BZF_PROTOCOL_H
#define BZF_PROTOCOL_H

#include "common.h"

// magic to identify a bzflag server.  first four characters are the
// bzflag magic.  the last four give the version number.  the first
// digit is the major release, the second and third digits are the
// minor release, and the last character is the revision.  The major
// number should increment when there've been sweeping changes in the
// whole program.  The minor release should increment for smaller
// changes that make the new version incompatible with older servers
// or clients.  The revision should be incremented for minor changes
// that don't cause incompatibility.
// adding new flags or changing the communication protocol requires
// minor release number changes.  adding new graphics effects normally
// only requires incrementing the revision number.

// well known service port number for bzflag server
const int       ServerPort = 5154;

// port for udp broadcasts used to find servers on local lan
const int       BroadcastPort = 5154;

// URL for default list server
const char* const   DefaultListServerURL = "https://my.bzflag.org/db/";
const char* const   DefaultMasterBanURL = "https://www.bzflag.org/master-bans.txt";
const char* const   DefaultMOTDServer = "https://www.bzflag.org/motd.php";

// maximum size of any message (including header and length fields)
const int       MaxPacketLen = 1024;
const int       MaxUDPPacketLen = 68;

// the banned tag
const char* const   BanRefusalString = "REFUSED:";

// player attributes for the MsgPlayerInfo message
enum PlayerAttribute
{
    IsRegistered = 1 << 0,
    IsVerified   = 1 << 1,
    IsAdmin      = 1 << 2
};

// null message code -- should never be sent
const uint16_t      MsgNull = 0x0000;

// server message codes
const uint16_t      MsgAccept = 0x6163;             // 'ac'
const uint16_t      MsgAdminInfo = 0x6169;          // 'ai'
const uint16_t      MsgAlive = 0x616c;              // 'al'
const uint16_t      MsgAddPlayer = 0x6170;          // 'ap'
const uint16_t      MsgAutoPilot = 0x6175;          // 'au'
const uint16_t      MsgCaptureFlag = 0x6366;        // 'cf'
const uint16_t      MsgCustomSound = 0x6373;        // 'cs'
const uint16_t      MsgDropFlag = 0x6466;           // 'df'
const uint16_t      MsgFlagDropped = 0x6664;        // 'fd'
const uint16_t      MsgEnter = 0x656e;              // 'en'
const uint16_t      MsgExit = 0x6578;               // 'ex'
const uint16_t      MsgFlagType = 0x6674;           // 'ft'
const uint16_t      MsgFlagUpdate = 0x6675;         // 'fu'
const uint16_t      MsgFetchResources = 0x6672;     // 'fr'
const uint16_t      MsgRequestFlag = 0x7266;        // 'rf'
const uint16_t      MsgFireShot = 0x6673;           // 'fs'
const uint16_t      MsgGrantFlag = 0x6766;          // 'gf'
const uint16_t      MsgGMUpdate = 0x676d;           // 'gm'
const uint16_t      MsgGameSettings = 0x6773;       // 'gs'
const uint16_t      MsgGameTime = 0x6774;           // 'gt'
const uint16_t      MsgHandicap = 0x6863;           // 'hc'
const uint16_t      MsgKilled = 0x6b6c;             // 'kl'
const uint16_t      MsgLagState = 0x6c73;           // 'ls'
const uint16_t      MsgSendChat = 0x7343;           // 'sC'
const uint16_t      MsgReceiveChat = 0x7243;        // 'rC'
const uint16_t      MsgNearFlag = 0x4e66;           // 'Nf'
const uint16_t      MsgNewRabbit = 0x6e52;          // 'nR'
const uint16_t      MsgNegotiateFlags = 0x6e66;     // 'nf'
const uint16_t      MsgPause = 0x7061;              // 'pa'
const uint16_t      MsgPlayerInfo = 0x7062;         // 'pb'
const uint16_t      MsgPlayerUpdate = 0x7075;       // 'pu'
const uint16_t      MsgPlayerUpdateSmall = 0x7073;  // 'ps'
const uint16_t      MsgQueryGame = 0x7167;          // 'qg'
const uint16_t      MsgQueryPlayers = 0x7170;       // 'qp'
const uint16_t      MsgReject = 0x726a;             // 'rj'
const uint16_t      MsgRemovePlayer = 0x7270;       // 'rp'
const uint16_t      MsgReplayReset = 0x7272;        // 'rr'
const uint16_t      MsgShotBegin = 0x7362;          // 'sb'
const uint16_t      MsgScore = 0x7363;              // 'sc'
const uint16_t      MsgScoreOver = 0x736f;          // 'so'
const uint16_t      MsgShotEnd = 0x7365;            // 'se'
const uint16_t      MsgSuperKill = 0x736b;          // 'sk'
const uint16_t      MsgSetVar = 0x7376;             // 'sv'
const uint16_t      MsgStealFlag = 0x7366;          // 'sf'
const uint16_t      MsgTimeUpdate = 0x746f;         // 'to'
const uint16_t      MsgTeleport = 0x7470;           // 'tp'
const uint16_t      MsgTransferFlag = 0x7466;       // 'tf'
const uint16_t      MsgTeamUpdate = 0x7475;         // 'tu'

const uint16_t      MsgAcceptWorld = 0x6177;        // 'aw'
const uint16_t      MsgSetWorld = 0x736D;           // 'sm'
const uint16_t      MsgGetWorld = 0x6777;           // 'gw'
const uint16_t      MsgStartWorld = 0x7377;         // 'sw'
const uint16_t      MsgEndWorld = 0x6577;           // 'ew'
const uint16_t      MsgWorldChunk = 0x7763;         // 'wc'

const uint16_t      MsgWantSettings = 0x7773;       // 'ws'
const uint16_t      MsgPortalAdd = 0x5061;          // 'Pa'
const uint16_t      MsgPortalRemove = 0x5072;       // 'Pr'
const uint16_t      MsgPortalUpdate = 0x5075;       // 'Pu'
const uint16_t      MsgSetTeam = 0x7374;            // 'st'

// world database codes
const uint16_t      WorldCodeHeader = 0x6865;       // 'he'
const uint16_t      WorldCodeBase = 0x6261;         // 'ba'
const uint16_t      WorldCodeBox = 0x6278;          // 'bx'
const uint16_t      WorldCodeEnd = 0x6564;          // 'ed'
const uint16_t      WorldCodeLink = 0x6c6e;         // 'ln'
const uint16_t      WorldCodePyramid = 0x7079;      // 'py'
const uint16_t      WorldCodeMesh = 0x6D65;         // 'me'
const uint16_t      WorldCodeArc = 0x6172;          // 'ar'
const uint16_t      WorldCodeCone = 0x636e;         // 'cn'
const uint16_t      WorldCodeSphere = 0x7370;       // 'sp'
const uint16_t      WorldCodeTetra = 0x7468;        // 'th'
const uint16_t      WorldCodeTeleporter = 0x7465;   // 'te'
const uint16_t      WorldCodeWall = 0x776c;         // 'wl'
const uint16_t      WorldCodeWeapon = 0x7765;       // 'we'
const uint16_t      WorldCodeZone = 0x7A6e;         // 'zn'
const uint16_t      WorldCodeGroup = 0x6772;        // 'gr'
const uint16_t      WorldCodeGroupDefStart = 0x6473;// 'ds'
const uint16_t      WorldCodeGroupDefEnd = 0x6465;  // 'de'

// world database sizes
const uint16_t      WorldSettingsSize = 30;
const uint16_t      WorldCodeHeaderSize = 10;
const uint16_t      WorldCodeBaseSize = 31;
const uint16_t      WorldCodeWallSize = 24;
const uint16_t      WorldCodeBoxSize = 29;
const uint16_t      WorldCodeEndSize = 0;
const uint16_t      WorldCodePyramidSize = 29;
const uint16_t      WorldCodeMeshSize = 0xA5;  // dummy value, sizes are variable
const uint16_t      WorldCodeArcSize = 85;
const uint16_t      WorldCodeConeSize = 65;
const uint16_t      WorldCodeSphereSize = 53;
const uint16_t      WorldCodeTetraSize = 66;
const uint16_t      WorldCodeTeleporterSize = 34;
const uint16_t      WorldCodeLinkSize = 4;
const uint16_t      WorldCodeWeaponSize = 24;  // basic size, not including lists
const uint16_t      WorldCodeZoneSize = 34;    // basic size, not including lists

// ping packet sizes, codes and structure
const uint16_t      MsgPingCodeReply = 0x0303;
const uint16_t      MsgPingCodeRequest = 0x0404;

// rejection codes
const uint16_t      RejectBadRequest = 0x0000;
const uint16_t      RejectBadTeam = 0x0001;
const uint16_t      RejectBadType = 0x0002;
const uint16_t      RejectBadMotto = 0x0003;
const uint16_t      RejectTeamFull = 0x0004;
const uint16_t      RejectServerFull = 0x0005;
const uint16_t      RejectBadCallsign = 0x0006;
const uint16_t      RejectRepeatCallsign = 0x0007;
const uint16_t      RejectRejoinWaitTime = 0x0008;
const uint16_t      RejectIPBanned = 0x0009;
const uint16_t      RejectHostBanned = 0x000A;
const uint16_t      RejectIDBanned = 0x000B;

// sound type codes
const uint16_t      LocalCustomSound = 0x0001;


// death by obstacle
// FIXME: really a killed reason, NOT a message type.
// should not be here at all, should be in BlowedUpReason
const uint16_t      PhysicsDriverDeath = 0x7064;        // 'pd'


// request for additional UDP link
const uint16_t      MsgUDPLinkRequest = 0x6f66;     // 'of'
const uint16_t      MsgUDPLinkEstablished = 0x6f67;     // 'og'

// lag ping sent by server to client and reply from client
const uint16_t      MsgLagPing = 0x7069;            // 'pi'

typedef uint8_t     PlayerId;
const int       PlayerIdPLen = sizeof(PlayerId);
const int       ServerIdPLen = 8;

// 54 bytes
const int PlayerUpdatePLenMax =
    sizeof(float) +     // timestamp
    PlayerIdPLen +      // player id
    sizeof(int32_t) +   // order
    sizeof(int16_t) +   // status
    sizeof(float) * 3 + // position             (or int16_t * 3)
    sizeof(float) * 3 + // velocity             (or int16_t * 3)
    sizeof(float) +     // angle                (or int16_t)
    sizeof(float) +     // angular velocity     (or int16_t)
    sizeof(int16_t) +   // jump jets          (conditional)
    sizeof(int32_t) +   // physics driver     (conditional)
    sizeof(int16_t) +   // user speed         (conditional)
    sizeof(int16_t) +   // user angular velocity  (conditional)
    sizeof(uint8_t);    // sounds         (conditional)

/* server communication protocol:
  --> incoming messages (to server)
  <-- outgoing messages to single player
  <== outgoing messages to all players

player to server messages:
  MsgEnter      player is joining game
            --> id, type, team, name, motto
            <-- MsgReject (if rejected)
            <-- MsgAccept (if accepted)
            if accepted, new player is sent (following MsgAccept):
            <-- MsgTeamUpdate (one per team)
            <-- MsgFlagUpdate (one per existing flag)
            <-- MsgAddPlayer (one per already joined player)
            add, finally, sent to all:
            <== MsgAddPlayer (player being accepted)
  MsgExit       player is signing off
            --> /id/
            <== MsgRemovePlayer
  MsgGetWorld       request for playing field database
            --> bytes read so far
            <-- MsgGetWorld
  MsgQueryGame      request for game state
            <-- MsgQueryGame
  MsgQueryPlayers   request for player list
            <-- MsgQueryPlayers
  MsgAlive      player says he's coming alive
            --> /id,
            <== MsgAlive
  MsgKilled     player says he's been killed
            --> /id,/ killer-id, reason, killer-shot-id
            <== MsgKilled
  MsgNewRabbit      player is relinquishing rabbitship
  MsgRequestFlag       player wants to grab flag
            --> /id,/ flag
            <== MsgGrantFlag
  MsgDropFlag       player wants to drop flag
            --> /id,/ position
            <== MsgFlagDropped
            <== MsgFlagUpdate
  MsgCaptureFlag    player captured flag
            --> /id,/ team (team flag was taken to)
            <== MsgCaptureFlag
            <== MsgFlagUpdate
  MsgSetVar     <== count/[name/value]*
  MsgFireShot      player has fired a shot
            --> FiringInfo
            <== MsgShotBegin
  MsgShotEnd        shot has terminated
            --> shooter id, shot number, reason
            <== MsgShotEnd
  MsgTeleport       player has teleported
            --> /id,/ from-teleporter, to-teleporter
            <== MsgTeleport
  MsgSendChat        player is sending a message
            --> /id,/ target-id/team-id, message string
            <== MsgReceiveChat
  MsgWantWHash      (player wants md5 of world file
            -->
  MsgNegotiateFlags -->flagCount/[flagabbv]
  MsgPause      -->true or false

server to player messages:
  MsgSuperKill      player must disconnect from server
            <== <none>
  MsgTimeUpdate     game time left, if == 0 player is dead and can't restart
            <== time (left, in seconds)
  MsgScoreOver      score limit reached, player is dead and can't restart
            <== id (winner), team (winner)
  MsgAccept     player request is accepted
            <== <none>
  MsgReject     player request is rejected
            <== <none>
  MsgAddPlayer      notification of new tank in game
            <== id, type, team, name, motto
  MsgRemovePlayer   player has exited the server
            <== id
  MsgAdminInfo      update of players' IP addresses
            only sent to players with the PLAYERLIST permission.
            <-- count, [chunklen, id, bitfield, address]*
  MsgPlayerInfo     update of players status
            <-- count, [id, bitfield]*
  MsgFlagUpdate     update of flag info
            <== count, [flag, flag-info]*
  MsgTeamUpdate     update of team info
            <== teamcount, [team, team-info]
  MsgGetWorld       chunk of world database
            <-- bytes left, next 256 byte chunk of world database
  MsgAlive      player is alive
            <== id, position, forward-vector
  MsgKilled     player is dead
            <== id (victim id), killer-id, reason, killer-shot-id
  MsgGantFlag       notification that flag is grabbed
            <== id (grabber), flag, flag-info
  MsgFlagDropped       notification that flag is in air
            <== id (dropper), flag, flag-info
  MsgCaptureFlag    notification that flag has been captured
            <== id (capturer), flag, team
  MsgShotBegin      some player has fired a shot
            <== FiringInfo
  MsgShotEnd        shot has expired
            <== id (shooter id), shot number, reason
  MsgScore      player score has changed
            <== num-scores [id (player id), wins, losses, tks]*n
  MsgTeleport       player has teleported
            <== id, from-teleporter, to-teleporter
  MsgReceiveChat        message to players
            <== from-id, to-id/team-id, message string
  MsgQueryGame      game status
  MsgQueryPlayers   list of players
  MsgWantWHash      md5 digest of world file
            <== temp|perm, digest
  MsgNegotiateFlags <== flagCount/[flagabbv]
  MsgNewRabbit      a new rabbit has been anointed
            <== id
  MsgPause      <== id/true or false
*/

#endif // BZF_PROTOCOL_H

// Local Variables: ***
// mode: C++ ***
// tab-width: 4 ***
// c-basic-offset: 4 ***
// indent-tabs-mode: nil ***
// End: ***
// ex: shiftwidth=4 tabstop=4
